#include "mem_buffer.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct buffer_map_element {
     bool lock;
     size_t sz;
     void *start;
     struct buffer_map_element *next;
} buffer_map_element_t;

buffer_map_element_t *buffer_map_head;
void *buffer;


void buffer_map_create(size_t sz) {
     buffer_map_head = (buffer_map_element_t *)malloc(sizeof(buffer_map_element_t));

     buffer_map_head->lock = false;
     buffer_map_head->sz = sz;
     buffer_map_head->start = buffer;
     buffer_map_head->next = NULL;
}

void buffer_map_remove() {
     buffer_map_element_t *current = buffer_map_head;
     buffer_map_element_t *next;
	  
     while (current != NULL) {
	  next = current->next;
	  free(current);
	  current = next;
     }
}

void *buffer_map_add(size_t sz) {
     buffer_map_element_t *current = buffer_map_head;
     
     while ((sz > current->sz)||(current->lock == true)) {
	  current = current->next;
	  if (current == NULL) return NULL;
     }
     
     if (sz == current->sz) {
	  current->lock = true;
	  current->sz = sz;
	  return current->start;
     }
     
     buffer_map_element_t *new_element = (buffer_map_element_t *)malloc(sizeof(buffer_map_element_t));
     
     new_element->lock = false;
     new_element->sz = current->sz - sz;
     new_element->start = current->start + sz;
     new_element->next = current->next;
     
     current->lock = true;
     current->sz = sz;
     current->next = new_element;
     return current->start; 
}

size_t buffer_map_delete(void *address){
     buffer_map_element_t *current = buffer_map_head;
     buffer_map_element_t *previous = NULL;
     
     while (current->start != address) {
	  previous = current;
	  current = current->next;
	  if (current == NULL) return 0;
     }
     
     size_t delete_size = current->sz;

     if ((current->next == NULL)||(current->next->lock == true)) {
	  if ((previous == NULL)||(previous->lock == true)) current->lock = false;

	  else {
	       previous->next = current->next;
	       previous->sz += current->sz;
	       free(current);
	  }
     }
     
     else {
	  if ((previous == NULL)||(previous->lock == true)) {
	       buffer_map_element_t *next = current->next;
	       current->lock = false;
	       current->sz += next->sz;
	       current->next = next->next;
	       free(next);
	  }
	  
	  else {
	       buffer_map_element_t *next = current->next;
	       previous->next = next->next;
	       previous->sz += current->sz + next->sz;
	       free(current);
	       free(next);
	  }
     }
     return delete_size;
}

void buffer_poisoning(void *address, size_t sz) {
     size_t i;
     char c = '0';
     for (i = 0; i < sz; i++) strncpy(address + i, &c, 1);
}



int buffer_create(size_t mem_size) {
     buffer = malloc(mem_size);
     buffer_map_create(mem_size);

     if (buffer == NULL) return 0;
     
     return 1;
}

void buffer_remove() {
     free(buffer);
     buffer_map_remove();
}

void *buffer_write(void *buf, size_t sz) {
     void *address = buffer_map_add(sz);
     if (address == NULL) return NULL;
     if (buf != NULL) strncpy(address, buf, sz);
     return address;
}

void buffer_delete(void *address) {
     /*size_t sz = */buffer_map_delete(address);
     //buffer_poisoning(address, sz);
}


